﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace S7Communication
{
    public class PLC
    {
        /// <summary>
        /// 目标引用，唯一标识
        /// </summary>
        public ushort destinationRef;
        /// <summary>
        /// 源引用，唯一标识
        /// </summary>
        public ushort sourcenRef;
        ///<summary>
        /// 机架号
        /// </summary>
        public ushort rack;
        /// <summary>
        /// 槽号
        /// </summary>
        public ushort solt;
        /// <summary>
        /// 调用的最大并行作业数
        /// </summary>
        public ushort maxAmQCalling;
        /// <summary>
        /// 已调用的最大并行作业数
        /// </summary>
        public ushort maxAmQCalled;
        /// <summary>
        /// 协商pdu长度
        /// </summary>
        public ushort pduLength;
        /// <summary>
        /// 请求序列
        /// </summary>
        public ushort sequenceID;
        object sequenceLocker = new object();
        /// <summary>
        /// 序列索引
        /// </summary>
        private Socket socket;
        private IPEndPoint remoteEndPoint;
        private object locker = new object();
        private bool isSocketInited;
        private bool isConected;
        private int timeOut;
        public PLC(IPAddress ip, int port, ushort rack, ushort solt)
        {
            this.rack = rack;
            this.solt = solt;
            remoteEndPoint = new IPEndPoint(ip, port);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            isSocketInited = false;
            isConected = false;
            timeOut = 10000;
        }

        public void Read(VariablesManager variablesManager)
        {
            variablesManager.Load(out List<List<Variable>> variablesGroups);
            lock (locker)
            {
                //建立通信连接
                Task.Run(() =>
                {
                    Connect();
                });
                int lastTime = Environment.TickCount;
                while (!isConected)
                {
                    if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                    {
                        throw new Exception("连接超时");
                    }
                    Task.Delay(0);
                }
                ArraySegment<byte> buffer;
                foreach (var items in variablesGroups)
                {
                    //发送读取请求
                    ushort sequenceId = SequenceID();
                    bool isSend = false;
                    Task.Run(() =>
                    {
                        socket.Send(SendBufferCompile.ReqReadBuffer(destinationRef, sequenceId, items.ToArray()));
                        isSend = true;
                    });
                    lastTime = Environment.TickCount;
                    while (!isSend)
                    {
                        if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                        {
                            throw new Exception("发送超时");
                        }
                        Task.Delay(0);
                    }
                    //接收读取响应
                    buffer = new ArraySegment<byte>();
                    int length = 0;
                    bool isRead = false;
                    lastTime = Environment.TickCount;
                    Task.Run(() =>
                    {
                        length = socket.Receive(buffer);
                        isRead = true;
                    });
                    while (!isRead)
                    {
                        if (lastTime < Environment.TickCount || Environment.TickCount > lastTime + timeOut)
                        {
                            throw new Exception("接收超时");
                        }
                        Task.Delay(0);
                    }
                    //分析接数据
                    RecieveBufferAnalysis bufferAnalysis = new RecieveBufferAnalysis();
                    bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                    if (bufferAnalysis.AnalysisData.dt_header_sequenceID == sequenceId)
                    {
                        for (int i = 0; i < items.Count; i++)
                        {

                            if (items[i].itemLength == bufferAnalysis.AnalysisData.dt_items[i].dataLength && bufferAnalysis.AnalysisData.dt_items[i].returnCode == DT_RETRUN_CODE.Success)
                            {
                                variablesManager.Updata(items[i].name, bufferAnalysis.AnalysisData.dt_items[i].data, bufferAnalysis.AnalysisData.dt_items[i].returnCode);
                            }
                        }
                    }
                }
                socket.Disconnect(false);
            }
        }

        private bool Connect()
        {
            try
            {
                //连接
                socket.Connect(remoteEndPoint);
                //清除缓存区数据
                if (socket.Available > 0)
                {
                    byte[] bufferTemp = new byte[socket.Available];
                    socket.Receive(bufferTemp);
                }
                //发送s7连接
                socket.Send(SendBufferCompile.ReqConnectBuffer(209, 209, 0, 1));
                //接收s7连接确认
                ArraySegment<byte> buffer = new ArraySegment<byte>();
                int length = socket.Receive(buffer);
                //分析接数据
                RecieveBufferAnalysis bufferAnalysis = new RecieveBufferAnalysis();
                bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                if (bufferAnalysis.AnalysisData.cotp_pduType != COTP_PDU_TYPE.DT) return false;
                rack = bufferAnalysis.AnalysisData.cotp_rack;
                solt = bufferAnalysis.AnalysisData.cotp_solt;
                //发送s7建立通信
                ushort sequenceId = SequenceID();
                socket.Send(SendBufferCompile.ReqStepCommunicationBuffer(209, sequenceId));
                //接收s7建立通信
                buffer = new ArraySegment<byte>();
                length = socket.Receive(buffer);
                //分析接数据
                bufferAnalysis = new RecieveBufferAnalysis();
                bufferAnalysis.BufferAnalysisAsync(buffer.Take(length).ToArray());
                //建立通信成功
                if (bufferAnalysis.AnalysisData.cotp_pduType != COTP_PDU_TYPE.DT) return false;
                if (bufferAnalysis.AnalysisData.dt_header_sequenceID != sequenceId) return false;
                if (bufferAnalysis.AnalysisData.dt_parameter_function != DT_PARAMETER_FUCTION.Setup_Communication) return false;
                maxAmQCalling = bufferAnalysis.AnalysisData.dt_stepCommunication_maxAmQCalling;
                maxAmQCalled = bufferAnalysis.AnalysisData.dt_stepCommunication_maxAmQCalled;
                pduLength = bufferAnalysis.AnalysisData.dt_stepCommunication_pduLength;
                return true;
            }
            catch { return false; }
        }

        private ushort SequenceID()
        {
            lock (sequenceLocker)
            {
                return (ushort)sequenceID++;
            }
        }
    }

}
